package io.dogstudio.processor;

import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import org.checkerframework.checker.units.qual.C;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

import io.dogstudio.annotation.BindView;
import io.dogstudio.contants.Constants;

@AutoService(Processor.class)
public class ViewBindingProcessor extends AbstractProcessor {
	private static final String VIEW_CLASS_NAME = "android.view.View";
	private static final String ACTIVITY_CLASS_NAME = "android.app.Activity";

	private Elements mElementUtils;
	private Filer mFiler;
	private Messager mMessager;
	private Types mTypeUtils;

	@Override
	public Set<String> getSupportedAnnotationTypes() {
		Set<String> set = new HashSet<>();
		set.add(BindView.class.getCanonicalName());
		return set;
	}

	@Override
	public SourceVersion getSupportedSourceVersion() {
		return SourceVersion.latestSupported();
	}

	@Override
	public synchronized void init(ProcessingEnvironment processingEnv) {
		super.init(processingEnv);
		mElementUtils = processingEnv.getElementUtils();
		mFiler = processingEnv.getFiler();
		mMessager = processingEnv.getMessager();
		mTypeUtils = processingEnv.getTypeUtils();
	}

	@Override
	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
		if (roundEnv.processingOver()) {
			return false;
		}
		/**
		 * key Activity对象
		 * value Set 包含需要binding的View对象
		 */
		Map<TypeElement, Set<Element>> routesMap = new HashMap<>();

		Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
		if (elements != null && elements.size() > 0) {
			for (Element item : elements) {
				// 获取父级容器
				TypeElement typeElement = (TypeElement) item.getEnclosingElement();
				// 获取当前元素的 配置信息
				//				BindView bindView = item.getAnnotation(BindView.class);
				//				int id = bindView.value();
				//				print("item type:" + item.asType().getClass());
				//				String viewType = ClassName.get(item.asType()).toString();
				//				Name viewName = item.getSimpleName();
				//				print("viewName:" + viewName + ", viewType:" + viewType + ", id:" + id);

				if (routesMap.get(typeElement) != null) {
					routesMap.get(typeElement).add(item);
				} else {
					Set<Element> set = new HashSet<>();
					set.add(item);
					routesMap.put(typeElement, set);
				}
			}
		}
		for (Map.Entry<TypeElement, Set<Element>> entry : routesMap.entrySet()) {
			TypeElement typeElement = entry.getKey();
			Set<Element> set = entry.getValue();

			TypeMirror activityMirror = mElementUtils.getTypeElement(ACTIVITY_CLASS_NAME).asType();
			TypeMirror viewMirror = mElementUtils.getTypeElement(VIEW_CLASS_NAME).asType();
			log("activityMirror:" + activityMirror.toString());
			log("viewMirror:" + viewMirror.toString());

			TypeMirror targetMirror = mElementUtils.getTypeElement(typeElement.getQualifiedName())
					.asType();

			log("targetMirror:" + targetMirror.toString());
			print("targetMirror pkgName:" + ClassName.get(typeElement).packageName());


			// 创建类
			TypeSpec.Builder classBuilder = TypeSpec
					.classBuilder(typeElement.getSimpleName().toString() + Constants.BUFFER)
					.addModifiers(Modifier.PUBLIC);
			//创建bind函数
			MethodSpec.Builder bindBuilder = MethodSpec.methodBuilder("bind")
					.addModifiers(Modifier.PUBLIC)
					.addParameter(TypeName.get(targetMirror), "activity").returns(void.class);
			for (Element item : set) {
				BindView bindView = item.getAnnotation(BindView.class);
				if (bindView != null) {
					String fieldName = item.getSimpleName().toString();
					int resId = bindView.value();
					bindBuilder.addStatement(CodeBlock.builder()
							.add("activity.$L = ($L)activity.findViewById($L)", fieldName,
									ClassName.get(item.asType()).toString(), resId).build());
				}
			}
			classBuilder.addMethod(bindBuilder.build());
			TypeSpec typeSpec = classBuilder.build();

			// 获取对应的包名
			JavaFile javaFile = JavaFile
					.builder(ClassName.get(typeElement).packageName(), typeSpec).build();
			try {
				javaFile.writeTo(System.out);
				javaFile.writeTo(mFiler);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return true;
	}


	/**
	 * 日志输出信息
	 *
	 * @param msg
	 */
	public void log(String msg) {
		if (mMessager == null) {
			return;
		}
		mMessager.printMessage(Diagnostic.Kind.NOTE, "ViewBindingProcessor >> " + msg);
	}

	public void print(String msg) {
		System.out.println(msg);
	}

}